/*
 * Copyright (c) 2017, Respective Authors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#pragma once
#include <eos/chain/types.hpp>
#include <eos/chain/message.hpp>

#include <numeric>

namespace eos { namespace chain {

   /**
    * @defgroup transactions Transactions
    *
    * All transactions are sets of messages that must be applied atomically (all succeed or all fail). Transactions
    * must refer to a recent block that defines the context of the operation so that they assert a known
    * state-precondition assumed by the transaction signers.
    *
    * Rather than specify a full block number, we only specify the lower 16 bits of the block number which means you
    * can reference any block within the last 65,536 blocks which is 2.2 days with a 3 second block interval.
    *
    * All transactions must expire so that the network does not have to maintain a permanent record of all transactions
    * ever published. A transaction may not have an expiration date too far in the future because this would require
    * keeping too much transaction history in memory.
    *
    * The block prefix is the first 4 bytes of the block hash of the reference block number, which is the second 4
    * bytes of the @ref block_id_type (the first 4 bytes of the block ID are the block number)
    *
    * @note It is not recommended to set the @ref ref_block_num, @ref ref_block_prefix, and @ref expiration
    * fields manually. Call @ref set_expiration instead.
    *
    * @{
    */

   /**
    * @brief A generated_transaction is a transaction which was internally generated by the blockchain, typically as a
    * result of running a contract.
    *
    * When contracts run and seek to interact with other contracts, or mutate chain state, they generate transactions
    * containing messages which effect these interactions and mutations. These generated transactions are automatically
    * generated by contracts, and thus are authorized by the script that generated them rather than by signatures. The
    * generated_transaction struct records such a transaction.
    *
    * These transactions are generated while processing other transactions. The generated transactions are assigned a
    * sequential ID, then stored in the block that generated them. These generated transactions can then be included in
    * subsequent blocks by referencing this ID.
    */
   struct generated_transaction : public Transaction {
      generated_transaction_id_type id;

      digest_type merkle_digest() const;
   };

   /**
    * @brief A transaction with signatures
    *
    * signed_transaction is a transaction with an additional manifest of authorizations included with the transaction,
    * and the signatures backing those authorizations.
    */
   struct SignedTransaction : public types::SignedTransaction {
      using types::SignedTransaction::SignedTransaction;

      /// Calculate the digest for a transaction
      digest_type         digest()const;
      transaction_id_type id()const;
      /// Calculate the digest used for signature validation
      digest_type         sig_digest(const chain_id_type& chain_id)const;

      void set_reference_block(const block_id_type& reference_block);
      bool verify_reference_block(const block_id_type& reference_block)const;

      /** signs and appends to signatures */
      const signature_type& sign(const private_key_type& key, const chain_id_type& chain_id);

      /** returns signature but does not append */
      signature_type sign(const private_key_type& key, const chain_id_type& chain_id)const;

      flat_set<public_key_type> get_signature_keys(const chain_id_type& chain_id)const;

      template <typename T>
      void setMessage(int messageIndex, const TypeName& type, T&& value) {
         Message m(messages[messageIndex]);
         m.set(type, std::forward<T>(value));
         messages[messageIndex] = m;
      }
      template <typename T>
      T messageAs(int messageIndex) {
         Message m(messages[messageIndex]);
         return m.as<T>();
      }
      template <typename... Args>
      void emplaceMessage(Args&&... a) {
         Message m(std::forward<Args>(a)...);
         messages.emplace_back(m);
      }

      /**
       * Removes all messages, signatures, and authorizations
       */
      void clear() { messages.clear(); signatures.clear(); authorizations.clear(); }

      digest_type merkle_digest()const;
   };

   /// @} transactions group

} } // eos::chain

FC_REFLECT(eos::chain::generated_transaction, (id))
FC_REFLECT_DERIVED(eos::chain::SignedTransaction, (eos::types::SignedTransaction), )
